MONGOID-5057 Add support for has_many :through#6151
Open
jamis wants to merge 21 commits into
Open
Conversation
…y in HasOneThrough
…ions When eager_load is called with a HasOneThrough or HasManyThrough association, log a warning naming the through associations and fall back to the includes-style preload path, since $lookup does not support two-hop through queries.
…oped criteria The metadata-level criteria(base) method must mirror the contract established by HasMany and other associations: it takes the owner document and returns a Criteria scoped to that owner's related records. The previous split into an unscoped criteria/0 and a resolve(base) method meant that any caller using the standard Relatable interface (e.g. eager loaders) would receive an unscoped result. Also add sum/avg/min/max to the proxy's delegator list so that aggregation methods work consistently with other has_many proxies.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds read-only :through support to Mongoid referenced associations, enabling has_one :through and has_many :through with includes preloading support and a dedicated Mongoid::Errors::ReadonlyAssociation error for mutation attempts.
Changes:
- Introduces new association metadata/proxy/eager-loader implementations for
Referenced::HasOneThroughandReferenced::HasManyThrough. - Extends association macro dispatch to select
*_throughassociation types whenthrough:is provided. - Adds a localized
ReadonlyAssociationerror and updates eager loading behavior to fall back from$lookupwhen through associations are included.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/mongoid/errors/readonly_association_spec.rb | Adds specs for the new ReadonlyAssociation error messaging. |
| spec/mongoid/association/referenced/has_one_through/proxy_spec.rb | Tests has_one-through proxy behavior and eager loader selection. |
| spec/mongoid/association/referenced/has_one_through/eager_spec.rb | Verifies includes-based preloading for has_one-through. |
| spec/mongoid/association/referenced/has_one_through_spec.rb | Adds unit + integration coverage for has_one-through metadata and read-only setter. |
| spec/mongoid/association/referenced/has_many_through/proxy_spec.rb | Tests has_many-through proxy read/mutation API surface. |
| spec/mongoid/association/referenced/has_many_through/eager_spec.rb | Verifies includes-based preloading for has_many-through. |
| spec/mongoid/association/referenced/has_many_through_spec.rb | Adds unit + integration coverage for has_many-through metadata, ordering, and patterns. |
| spec/mongoid/association/macros_spec.rb | Ensures macros instantiate the correct *_through metadata when through: is present. |
| spec/mongoid/association/eager_loadable_spec.rb | Confirms eager_load warns and falls back to preload for through associations. |
| lib/mongoid/errors/readonly_association.rb | Implements new error class used for write attempts on through associations. |
| lib/mongoid/errors.rb | Requires the new error class. |
| lib/mongoid/association/referenced/has_one_through/proxy.rb | Adds proxy class for has_one-through association type. |
| lib/mongoid/association/referenced/has_one_through/eager.rb | Adds two-query preloader for has_one-through. |
| lib/mongoid/association/referenced/has_one_through.rb | Adds metadata/definition logic for has_one-through. |
| lib/mongoid/association/referenced/has_many_through/proxy.rb | Adds read-only proxy wrapper for has_many-through criteria. |
| lib/mongoid/association/referenced/has_many_through/eager.rb | Adds two-query preloader for has_many-through. |
| lib/mongoid/association/referenced/has_many_through.rb | Adds metadata/definition logic for has_many-through criteria building and ids getter. |
| lib/mongoid/association/referenced.rb | Requires the new through association implementations. |
| lib/mongoid/association/macros.rb | Routes has_one/has_many to through-metadata when through: is provided. |
| lib/mongoid/association/eager_loadable.rb | Detects through inclusions and falls back from $lookup eager loading with a warning. |
| lib/mongoid/association.rb | Adds internal THROUGH_MACRO_MAPPING for has_one/has_many. |
| lib/config/locales/en.yml | Adds i18n strings for the new readonly_association error. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Remove :scope from ASSOCIATION_OPTIONS on both through types; it was listed as valid but never applied, causing silent no-ops - Add HABTM guard to HasOneThrough#source_association to match the existing guard in HasManyThrough - Fix reverse-FK eager loaders (has_one and has_many) to use source_assoc.primary_key when collecting intermediate PKs and joining back to targets; through_assoc.primary_key is the owner's key and gives wrong results when a custom :primary_key is configured - Fix HasManyThrough#criteria reverse-FK branch with the same correction - Eager loaders now store a Proxy rather than a raw document/array, so the cached type after includes() matches the non-eager getter - HasManyThrough::Proxy accepts a preloaded: kwarg; enumerable methods (each, to_a, first, etc.) use the preloaded array when set, while query methods (where, pluck, etc.) always build a fresh Criteria
…hens RSpec embeds context description strings in the JSON output. Em-dash characters (U+2014, encoded as \xE2\x80\x94 in UTF-8) cause Encoding::InvalidByteSequenceError when CI reads tmp/rspec.json under LANG=C, where Encoding.default_external is US-ASCII.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements read-only
:throughassociations forhas_oneandhas_many, matching ActiveRecord's API:Both association types support
:sourceto override the inferred source name, and:class_namefor non-conventional class names.has_many :throughadditionally accepts:order. All through associations are read-only; write attempts raiseMongoid::Errors::ReadonlyAssociation. Eager loading via includes is supported for both types.The two query patterns supported for
has_many :throughare the join-model pattern (FK on the intermediate, e.g.belongs_to :patientonAppointment) and the reverse-FK pattern (FK on the target, e.g.has_many :readersonBook).Out of Scope
:scopehas_many :throughthat is itself a through associationhas_and_belongs_to_manyas the source association.